#include "laurin-80211-frame.h"


#define FIXED_PARAMETERS_OFFSET 12

// ---------- from kismet source, copyright holds ----------/

typedef struct {
    u_int8_t timestamp[8];													// timestamp 8 bytes

    // This field must be converted to host-endian before being used
    unsigned int beacon : 16 __attribute__ ((packed));						// beacon interval 2 bytes

    unsigned short agility : 1 __attribute__ ((packed));					// channel agility
    unsigned short pbcc : 1 __attribute__ ((packed));						// pbcc modulation allowed?
    unsigned short short_preamble : 1 __attribute__ ((packed));				// short preamble
    unsigned short wep : 1 __attribute__ ((packed));						// wep support

    unsigned short unused2 : 1 __attribute__ ((packed));
    unsigned short unused1 : 1 __attribute__ ((packed));
    unsigned short ibss : 1 __attribute__ ((packed));						// ibss participation
    unsigned short ess : 1 __attribute__ ((packed));						// ess capabilities

    unsigned int coordinator : 8 __attribute__ ((packed));

} fixed_parameters;


static u_int ieee80211_mhz2ieee(u_int freq, u_int flags) {
	if (flags & IEEE80211_CHAN_2GHZ) {		/* 2GHz band */
		if (freq == 2484)
			return 14;
		if (freq < 2484)
			return (freq - 2407) / 5;
		else
			return 15 + ((freq - 2512) / 20);
	} else if (flags & IEEE80211_CHAN_5GHZ) {	/* 5Ghz band */
		return (freq - 5000) / 5;
	} else {					/* either, guess */
		if (freq == 2484)
			return 14;
		if (freq < 2484)
			return (freq - 2407) / 5;
		if (freq < 5000)
			return 15 + ((freq - 2512) / 20);
		return (freq - 5000) / 5;
	}
}

#define	IEEE80211_CHAN_FHSS		(IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_GFSK)
#define	IEEE80211_CHAN_A		(IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM)
#define	IEEE80211_CHAN_B		(IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_CCK)
#define	IEEE80211_CHAN_PUREG	(IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_OFDM)
#define	IEEE80211_CHAN_G		(IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_DYN)
#define	IEEE80211_CHAN_T		(IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM | IEEE80211_CHAN_TURBO)

#define	IEEE80211_IS_CHAN_FHSS(_flags)	((_flags & IEEE80211_CHAN_FHSS) == IEEE80211_CHAN_FHSS)
#define	IEEE80211_IS_CHAN_A(_flags)		((_flags & IEEE80211_CHAN_A) == IEEE80211_CHAN_A)
#define	IEEE80211_IS_CHAN_B(_flags)		((_flags & IEEE80211_CHAN_B) == IEEE80211_CHAN_B)
#define	IEEE80211_IS_CHAN_PUREG(_flags)	((_flags & IEEE80211_CHAN_PUREG) == IEEE80211_CHAN_PUREG)
#define	IEEE80211_IS_CHAN_G(_flags)		((_flags & IEEE80211_CHAN_G) == IEEE80211_CHAN_G)
#define	IEEE80211_IS_CHAN_T(_flags)		((_flags & IEEE80211_CHAN_T) == IEEE80211_CHAN_T)


#define BITNO_32(x) (((x) >> 16) ? 16 + BITNO_16((x) >> 16) : BITNO_16((x)))
#define BITNO_16(x) (((x) >> 8) ? 8 + BITNO_8((x) >> 8) : BITNO_8((x)))
#define BITNO_8(x) (((x) >> 4) ? 4 + BITNO_4((x) >> 4) : BITNO_4((x)))
#define BITNO_4(x) (((x) >> 2) ? 2 + BITNO_2((x) >> 2) : BITNO_2((x)))
#define BITNO_2(x) (((x) & 2) ? 1 : 0)
#define BIT(n)	(1 << n)


//-----------------------------------------------------------//




Laurin80211Frame::Laurin80211Frame()
{
	_allocated = false;

	_radio.ant_noise_db = 0;
	_radio.ant_signal_db = 0;
	_radio.carrier = CARRIER_UNKNOWN;
	_radio.tstf = 0;
	_radio.channel = 0;

	_frame = NULL;
	_radiotap = NULL;
	_type = 0;
	_subType = 0;
	_len = 0;

	// for ap
	_ssid = "";
	_ibss = true;
	_interval = 0;
	_encrypted = false;

	// addresses
	_source = NULL;
	_destination = NULL;
	_bssid = NULL;

}


Laurin80211Frame::Laurin80211Frame(const Laurin80211Frame& frame)
{
	_radio = frame._radio;
	_type = frame._type;
	_subType = frame._subType;

	_ssid = Glib::ustring(frame._ssid);
	_ibss = frame._ibss;
	_interval = frame._interval;
	_encrypted = frame._encrypted;

	_source = _destination = _bssid = NULL;

	if(_allocated)
	{
		if(_source)
			free(_source);
		if(_destination)
			free(_destination);
		if(_bssid)
			free(_bssid);
	}
	else
		_source = _destination = _bssid = NULL;

	if(frame._source)
	{
		_source = (u_int8_t*) malloc(ETH_ALEN);
		memcpy(_source, frame._source, ETH_ALEN);
		_allocated = true;
	}


	if(frame._destination)
	{
		_destination = (u_int8_t*) malloc(ETH_ALEN);
		memcpy(_destination, frame._destination, ETH_ALEN);
		_allocated = true;
	}

	if(frame._bssid)
	{
		_bssid = (u_int8_t*) malloc(ETH_ALEN);
		memcpy(_bssid, frame._bssid, ETH_ALEN);
		_allocated = true;
	}

	_radiotap = NULL;
	_frame = NULL;
	_len = 0;
}


Laurin80211Frame::~Laurin80211Frame()
{
	if(_bssid)
		free(_bssid);

	if(_destination)
		free(_destination);

	if(_source)
		free(_source);
}


bool Laurin80211Frame::setFrame(const u_char* frame, int len)
{


	if(len <  sizeof(struct ieee80211_radiotap_header))
		return false;

	_radiotap = (struct ieee80211_radiotap_header*) frame;


	if(len < _radiotap->it_len + sizeof(laurin_ieee80211_hdr) || _radiotap->it_version)
		return false;

	_frame = &frame[_radiotap->it_len];
	_len = len - _radiotap->it_len;

	return true;
}


bool Laurin80211Frame::parse80211FrameType()
{
	if(!_frame)
		return false;

	laurin_ieee80211_hdr* header = (laurin_ieee80211_hdr*) _frame;

	_type = WLAN_FC_GET_TYPE(header->frame_ctl);
	_subType = WLAN_FC_GET_STYPE(header->frame_ctl);

	switch(_type)
	{
		case IEEE80211_FTYPE_DATA:
			if(_subType == IEEE80211_STYPE_DATA)
				return _len > sizeof(laurin_ieee80211_hdr_3addr);

			return false;

		case IEEE80211_FTYPE_CTL:
			// TODO
			return false;

		case IEEE80211_FTYPE_MGMT:
			if(_subType & IEEE80211_STYPE_BEACON)
				// beacon have fixed parameters + tagged parameters. We check if it have at least the fixed parameters
				// and the ssid (first tagged parameters: 1 bytes for the index, and 1 byte for the length)
				return _len > IEEE80211_3ADDR_LEN + FIXED_PARAMETERS_OFFSET + 2;

	}

	return false;
}


bool Laurin80211Frame::parseRadiotapHeader()
{
	/*	- elaborate radio informations: antenna noise and signal etc...
		  we dissect the radiotap header, discarding all the informations that aren't necessary

		- most of the code is get from the kismet project (thanks!)

		- N.B we assume a little endian machine, no conversions are done
		  all the fields on the radiotap header are stored in little endian order.

    	- I consider only ONE bitmap on the radiotap it_present field,
		  if there are more, I discard they. this is not a sniffer, so the informations that
		  can be stored on the supplementary bitmap extensions aren't necessary:
		  if these fields aren't commonly used,  I can't use they. */


	if(!_radiotap)
		return false;

	u_int32_t present, next_present;
	u_int32_t* last_present;							// where last bitmap is
    enum ieee80211_radiotap_type bit;
    u_char *iter = NULL;

    // I need to know where the bitmaps ends.
    for(last_present = &_radiotap->it_present; ((*last_present) & BIT(IEEE80211_RADIOTAP_EXT)) != 0 &&
		((u_char*)(last_present + 1) <= (u_char*) _radiotap + _radiotap->it_len); last_present += sizeof(u_int32_t));


    iter = (u_char*) last_present + sizeof(u_int32_t);
    u_int16_t bitmap, mhz;


	// read the bitmap
    for (present = _radiotap->it_present; present; present = next_present)
    {
    	/* clear the least significant bit that is set */
    	next_present = present & (present - 1);

    	/* extract the least significant bit that is set */
    	bit = (enum ieee80211_radiotap_type) BITNO_32(present ^ next_present);

    	switch (bit)
    	{

			case IEEE80211_RADIOTAP_FLAGS:				// Properties of transmitted and received frames.
			case IEEE80211_RADIOTAP_RATE:				// TX/RX data rate.
			case IEEE80211_RADIOTAP_FHSS:				// The hop set and pattern for frequency-hopping radios.
			case IEEE80211_RADIOTAP_ANTENNA:			// Unitless indication of the Rx/Tx antenna for this packet
				// these informations doesn't concern our purposes, so skip they
				iter = iter + sizeof(char);
				break;


			case IEEE80211_RADIOTAP_LOCK_QUALITY:		// Quality of Barker code lock (Signal Quality)
				//skip
				iter = iter + sizeof(u_int16_t);
				break;


			case IEEE80211_RADIOTAP_DBM_ANTSIGNAL:		// RF signal power at the antenna
				_radio.ant_signal_db = *((u_int8_t*) iter);
				iter = iter + sizeof(char);
				break;


			case IEEE80211_RADIOTAP_DBM_ANTNOISE:		// RF noise power at the antenna
				_radio.ant_noise_db = *((u_int8_t*) iter);
				iter = iter + sizeof(char);
				break;


			case IEEE80211_RADIOTAP_DB_ANTSIGNAL:
				_radio.ant_signal_db = *((u_int8_t*) iter);
				iter = iter + sizeof(char);
				break;


			case IEEE80211_RADIOTAP_DB_ANTNOISE:
				_radio.ant_noise_db = *((u_int8_t*) iter);
				iter = iter + sizeof(char);
				break;


			case IEEE80211_RADIOTAP_CHANNEL:			// Tx/Rx frequency in MHz, followed by flags.

				mhz = *((u_int16_t*)iter);
				iter = iter + sizeof(u_int16_t);

				bitmap = *((u_int16_t*)iter);

				_radio.channel = (short) ieee80211_mhz2ieee(mhz, bitmap);


				if (IEEE80211_IS_CHAN_FHSS(bitmap))
					_radio.carrier = CARRIER_80211FHSS;
				else if (IEEE80211_IS_CHAN_A(bitmap))
					_radio.carrier = CARRIER_80211A;
				else if (IEEE80211_IS_CHAN_B(bitmap))
					_radio.carrier = CARRIER_80211B;
				else if (IEEE80211_IS_CHAN_PUREG(bitmap))
					_radio.carrier = CARRIER_80211G;
				else if (IEEE80211_IS_CHAN_G(bitmap))
					_radio.carrier = CARRIER_80211G;
				else if (IEEE80211_IS_CHAN_T(bitmap))
					_radio.carrier = CARRIER_80211A;
				else
					_radio.carrier = CARRIER_UNKNOWN;

				iter = iter + sizeof(u_int16_t);
				break;


			case IEEE80211_RADIOTAP_DBM_TX_POWER:		// Transmit power expressed as dBm
				iter = iter + sizeof(char);
				break;


			case IEEE80211_RADIOTAP_TX_ATTENUATION:		// Transmit power expressed as unitless distance from max power set at factory calibration
			case IEEE80211_RADIOTAP_DB_TX_ATTENUATION:
				iter = iter + sizeof(u_int16_t);
				break;


			// Value in microseconds of the MAC's 64-bit 802.11 Time Synchronization Function timer
			// when the first bit of the MPDU arrived at the MAC
			case IEEE80211_RADIOTAP_TSFT:
				_radio.tstf = *((u_int64_t*) iter);
				iter = iter + sizeof(u_int64_t);
				break;


			default:
				return false;
		}
	}

    return true;
}



bool Laurin80211Frame::parse80211Header()
{
	/*	elaborate the 80211 header informations,
		in the specific we get the bssid, source and destination address values.
		for doing so, we read the tods and fromds values and we act conseguently.
		we check if frame belongs to an ad-hoc network too. If so we return false.


	   | from ds  |  to ds |

	       0		   0			ad-hoc network frame ( or not leaving DS)
	       1		   0			from wired to wirelss frame (a frame from ds to a station via an AP)
	       1		   1			we are in the wireless distribution network, we don't support it.
	       0		   1	 		from wireless to wired frame (frame from station to DS via an AP)

	 */

	//TODO frame with less than 3 address

	laurin_ieee80211_hdr_3addr* header = (laurin_ieee80211_hdr_3addr*) _frame;

	_destination = (u_int8_t*) malloc(ETH_ALEN);
	_source = (u_int8_t*) malloc(ETH_ALEN);
	_bssid = (u_int8_t*) malloc(ETH_ALEN);

	_allocated = true;

	//we have captured only a 3 address packet!
	if(WLAN_FC_GET_FROMDS(header->frame_ctl))
	{
		if(!(WLAN_FC_GET_TODS(header->frame_ctl)))
		{
			memcpy(_destination, header->addr1 , ETH_ALEN);
			memcpy(_bssid, header->addr2, ETH_ALEN);
			memcpy(_source, header->addr3, ETH_ALEN);
		}
	}
	else
	{
		if(WLAN_FC_GET_TODS(header->frame_ctl))
		{
			memcpy(_bssid, header->addr1, ETH_ALEN);
			memcpy(_source, header->addr2, ETH_ALEN);
			memcpy(_destination, header->addr3 , ETH_ALEN);
		}
		else
		{
			memcpy(_destination, header->addr1 , ETH_ALEN);
			memcpy(_source, header->addr2, ETH_ALEN);
			memcpy(_bssid, header->addr3, ETH_ALEN);
		}
	}

	// management frames are only the beacon ones
	if(_type == IEEE80211_FTYPE_MGMT || _subType == IEEE80211_STYPE_BEACON)
	{

		// skip the header and read the frame
		fixed_parameters* fp = (fixed_parameters*) &header->payload;
		_ibss = (fp->ibss == 1 && fp->ess == 0);
		_interval = fp->beacon;
		_encrypted = fp->wep;


		// get tagged parameters (only ssid)
		u_char* tagged_para = (u_char*) & ((header->payload)[FIXED_PARAMETERS_OFFSET]);

		u_int8_t* ssid_len = (u_int8_t*) tagged_para + 1;	//skip the tag number

		if(*ssid_len <= 0 || (_len < IEEE80211_3ADDR_LEN + FIXED_PARAMETERS_OFFSET + 2 + *ssid_len))
			return false;

		char* s = (char*) malloc(*ssid_len);
		memcpy(s, tagged_para + 2 , *ssid_len);			// plus 2 bytes

		_ssid = Glib::ustring(s);
		free(s);
	}

	return true;

}


short Laurin80211Frame::getFrameType() const
{
	return _type;
}


short Laurin80211Frame::getFrameSubType() const
{
	return _subType;
}


u_int8_t* Laurin80211Frame::getBSSID() const
{
	return _bssid;
}


u_int8_t* Laurin80211Frame::getSourceAddress() const
{
	return _source;
}


u_int8_t* Laurin80211Frame::getDestinationAddress() const
{
	return _destination;
}


Glib::ustring Laurin80211Frame::getSSID() const
{
	return _ssid;
}


bool Laurin80211Frame::isNetworkEncrypted() const
{
	return _encrypted;
}


bool Laurin80211Frame::isIBBSFrame() const
{
	return _ibss;
}


int Laurin80211Frame::getInterval() const
{
	return _interval;
}


struct laurin_packet_radio_info Laurin80211Frame::getRadioInformations() const
{
	return _radio;
}
